package ntd.util.frame.javacv.tool;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.ftp.Ftp;
import cn.hutool.log.StaticLog;
import ntd.util.UtilCatch;
import ntd.util.UtilThreadPool;
import ntd.util.frame.javacv.ComVisionApi;
import ntd.util.frame.javacv.config.AiConfig;
import ntd.util.frame.javacv.remote.AiVisionRemote;
import ntd.util.util.ToolDuration;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

import java.io.*;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class ToolFtpSync {

    private static final String REMOTE_BASE_PATH = "";
    private CopyOnWriteArrayList<Ftp> ftpClients = CollectionUtil.newCopyOnWriteArrayList(new ArrayList<>());
    private volatile int num = 0;

    private String ip;
    private int port;
    private String username;
    private String password;

    public ToolFtpSync(String ip, int port, String username, String password) {
        this.ip = ip;
        this.port = port;
        this.username = username;
        this.password = password;
    }

    public static void main(String[] args) {
        List<AiVisionRemote> aiVisionRemotes = ComVisionApi.initAiRemote();
        for (AiVisionRemote aiVisionRemote : aiVisionRemotes) {
            ToolFtpSync ftpSync = aiVisionRemote.getFtpSync();
            List<String> list = ftpSync.lsFiles("/ai/data");
            StaticLog.info("共{}个文件", list.size());
        }
    }

    public synchronized Ftp ftp() {
        StaticLog.info("当前ftp可用链接数量 - {}", ftpClients.size());
        if (ftpClients.size() < 1) {
            StaticLog.info("当前ftp总链接数量 - {}", ++num);
            Ftp ftp = new Ftp(ip, port, username, password);
            ftp.getClient().enterLocalPassiveMode();
            ftp.getClient().setBufferSize(1024 * 1024 * 10);
            return ftp;
        }
        Ftp remove = ftpClients.remove(0);
        remove.reconnectIfTimeout();
        return remove;
    }

    public synchronized void rFtp(Ftp ftp) {
        ftpClients.add(ftp);
        StaticLog.info("ftp链接使用完成，归还后数量 - {}, 总数量 - {}", ftpClients.size(), num);
    }

    public FTPClient initFtpClient() {
        return UtilCatch.tryCatch(() -> {
            FTPClient ftpClient = new FTPClient();
            ftpClient.connect(ip, port);
            ftpClient.login(username, password);
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            ftpClient.setBufferSize(1024 * 1024 * 10);
            ftpClient.setControlEncoding("GBK"); // 编码格式
            ftpClient.enterLocalPassiveMode();
            //如果reply返回230就算成功了，如果返回530密码用户名错误或当前用户无权限下面有详细的解释。
            if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
                ftpClient.disconnect();
            }
            return ftpClient;
        });
    }

    public void close(FTPClient ftpClient) {
        UtilCatch.tryCatchNoRes(() -> {
            ftpClient.logout();
            ftpClient.disconnect();
            StaticLog.info("FtpClient关闭结果 - {}", FTPReply.isPositiveCompletion(ftpClient.getReplyCode()));
        });
    }

    public void upload(String path, String fileName, byte[] bytes) {
        Ftp ftpClient = ftp();
        UtilCatch.tryCatchNoRes(() -> {
            ToolDuration.duration(() -> {
                if (ArrayUtil.isEmpty(bytes)) {
                    return;
                }
                ftpClient.mkdir(path);
                ftpClient.upload(path, fileName, new ByteArrayInputStream(bytes));
            }, StrUtil.format("FTP-文件上传-{}/{}", path, fileName));
        }, () -> ftpClients.add(ftpClient));
    }

    public void uploadSync(String path) {
        Ftp ftpClient = ftp();
        UtilCatch.tryCatchNoRes(() -> ToolDuration.duration(() -> {
            File mkdir = FileUtil.mkdir(AiConfig.LOCAL_BASE_DATA_PATH + path);
            if (!FileUtil.exist(path) || !mkdir.isDirectory() || FileUtil.isDirEmpty(mkdir)) {
                return;
            }
            File[] files = FileUtil.ls(AiConfig.LOCAL_BASE_DATA_PATH + path);
            for (File file : files) {
                if (file.isDirectory()) {
                    uploadSync(StrUtil.format("{}/{}", path, file.getName()));
                } else {
                    ftpClient.upload(REMOTE_BASE_PATH + path, file.getName(), new FileInputStream(file));
                }
            }
        }, StrUtil.format("FTP-文件同步上传-{}", path)), () -> ftpClients.add(ftpClient));
    }

    public void downloadSync(String path) {
        List<String> list = lsFiles(path);
        StaticLog.info("本次共需下载[{}]个文件", list.size());
        for (String filePath : list) {
            UtilThreadPool.run(() -> {
                if (!handleDownload(filePath)) {
                    StaticLog.info("FTP - 下载文件[{}]失败", filePath);
                }
            });
        }
    }

    /**
     * 递归查询目录下的文件列表
     * @param path
     * @return
     */
    public List<String> lsFiles(String path) {
        // 检测需要下载的文件列表
        Ftp ftpClient = ftp();
        List<String> filePaths = new ArrayList<>();
        ToolDuration.duration(() -> {
            if (ftpClient.exist(path)) {
                FTPFile[] ftpFiles = ToolDuration.duration(() -> ftpClient.lsFiles(path), StrUtil.format("FTP - 获取文件列表 - {}", path));
                if (ArrayUtil.isEmpty(ftpFiles)) {
                    ToolDuration.duration(() -> ftpClient.delDir(path), StrUtil.format("FTP - 删除文件组 - {}", path));
                    return;
                }
                //目录或文件访问的数量限制
                ftpFiles = Arrays.stream(ftpFiles).limit(20).toArray(FTPFile[]::new);
                for (FTPFile ftpFile : ftpFiles) {
                    if (ftpFile.isDirectory()) {
                        String childrenPath = StrUtil.format("{}/{}", path, ftpFile.getName());
                        filePaths.addAll(lsFiles(childrenPath));
                    } else {
                        String filePath = StrUtil.format("{}/{}", path, ftpFile.getName());
                        filePaths.add(filePath);
                    }
                }
            }
        }, StrUtil.format("FTP - 文件检测 - {}", path));
        rFtp(ftpClient);
        return filePaths;
    }

    public boolean handleDownload(String path) {
        Ftp ftpClient = ftp();
        ToolDuration.duration(() -> {
            String localPath = StrUtil.format("{}/{}", AiConfig.LOCAL_BASE_DATA_TEMP_PATH, path);
            String finalPath = StrUtil.format("{}/{}", AiConfig.LOCAL_BASE_DATA_PATH, path);
            // 下载 - 特殊名字
            ftpClient.download(path, FileUtil.file(localPath));
            ftpClient.delFile(path);
            // 改为正式路径
            FileUtil.move(FileUtil.file(localPath), FileUtil.file(finalPath), true);
        }, StrUtil.format("FTP - 文件下载 - {}", path));
        rFtp(ftpClient);
        return true;
    }

    public void delDeep(String path) {
        Ftp ftpClient = ftp();
        UtilCatch.tryCatchNoRes(() -> {
            ToolDuration.duration(() -> {
                FTPFile[] ftpFiles = ToolDuration.duration(() -> ftpClient.lsFiles(path), StrUtil.format("开始获取删除列表 - {}", path));
                if (ArrayUtil.isEmpty(ftpFiles)) {
                    ftpClient.delDir(path);
                    return;
                }
                int index = 0;
                for (FTPFile ftpFile : ftpFiles) {
                    if (index++ > 100) {
                        return;
                    }
                    if (ftpFile.isDirectory()) {
                        String childrenPath = StrUtil.format("{}/{}", path, ftpFile.getName());
                        delDeep(childrenPath);
                    }
                }
            }, StrUtil.format("FTP-文件深度删除-{}", path));
        }, () -> ftpClients.add(ftpClient));
    }

    public void delSync(String path) {
        Ftp ftpClient = ftp();
        UtilCatch.tryCatchNoRes(() -> ToolDuration.duration(() -> {
            FileUtil.del(path);
            ftpClient.delDir(path);
        }, StrUtil.format("FTP-文件同步删除-{}", path)), () -> ftpClients.add(ftpClient));
    }
}
